﻿namespace Microsoft.VisualStudio.TestTools.UnitTesting
{
    using System;
    using System.Diagnostics.Contracts;

    /// <summary>
    /// A utility class that holds simple common tests such as parameters being <c>null</c>.
    /// </summary>
    public static class CommonTests
    {
        #region Public Methods
        /// <summary>
        /// Verifies that the action throws an <see cref="ArgumentException"/> for the specified argument name.
        /// </summary>
        /// <param name="action">The action that raises the <see cref="ArgumentException"/> exception.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentCannotBeEmptyString(Action action, string argumentName)
        {
            Contract.Requires(action != null);

            var exception = AssertEx.Throws<ArgumentException>(action);
            AssertEx.That(() => exception.Message == "Value cannot be an empty string.\r\nParameter name: " + argumentName);
        }

        /// <summary>
        /// Verifies that the action throws an <see cref="ArgumentException"/> for the specified argument name.
        /// </summary>
        /// <param name="action">The action to test with different argument values.</param>
        /// <param name="argumentName">The name of the argument.</param>
        /// <remarks>
        /// An empty string is tried as well as a string with a space and tab character.
        /// </remarks>
        public static void ArgumentCannotBeEmptyTrimmedString(Action<string> action, string argumentName)
        {
            Contract.Requires(action != null);
            Contract.Requires(argumentName != null);

            new string[]
            {
                string.Empty,
                " \t"
            }
            .Run(argumentValue =>
                {
                    var exception = AssertEx.Throws<ArgumentException>(() => action(argumentValue));
                    AssertEx.That(() => exception.Message == "Value cannot be an empty string.\r\nParameter name: " + argumentName);
                });
        }

        /// <summary>
        /// Verifies that the action throws an <see cref="ArgumentNullException"/> for the specified argument name.
        /// </summary>
        /// <param name="action">The action that raises the <see cref="ArgumentNullException"/> exception.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentCannotBeNull(Action action, string argumentName)
        {
            Contract.Requires(action != null);
            Contract.Requires(argumentName != null);

            var exception = AssertEx.Throws<ArgumentNullException>(action);
            AssertEx.That(() => exception.Message == "Value cannot be null.\r\nParameter name: " + argumentName);
        }

        /// <summary>
        /// Combines <see cref="CommonTests.ArgumentCannotBeNull"/> and <see cref="CommonTests.ArgumentCannotBeEmptyString"/>.
        /// </summary>
        /// <param name="action">The action to test with different argument values.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentCannotBeNullOrEmptyString(Action<string> action, string argumentName)
        {
            Contract.Requires(action != null);
            Contract.Requires(argumentName != null);

            CommonTests.ArgumentCannotBeNull(() => action(null), argumentName);
            CommonTests.ArgumentCannotBeEmptyString(() => action(string.Empty), argumentName);
        }

        /// <summary>
        /// Combines <see cref="CommonTests.ArgumentCannotBeNull"/> and <see cref="CommonTests.ArgumentCannotBeEmptyTrimmedString"/>.
        /// </summary>
        /// <param name="action">The action to test with different argument values.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentCannotBeNullOrEmptyTrimmedString(Action<string> action, string argumentName)
        {
            Contract.Requires(action != null);
            Contract.Requires(argumentName != null);

            CommonTests.ArgumentCannotBeNull(() => action(null), argumentName);
            CommonTests.ArgumentCannotBeEmptyTrimmedString(action, argumentName);
        }
        #endregion Public Methods
    }
}